package controller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JOptionPane;

import model.ActionsEnum;
import model.Bot;
import model.ExceptionsInGame;
import model.Human;
import model.Judge;
import model.Player;
import model.StatusEnum;
import model.Table;

public class Rozgrywka {

	private Table table;
	private int numPlayers;
	private int currentMax;

	private List<PlayerWindow> playerWindowsList;
	private List<String> playerIDsList;
	private List<Player> players = new ArrayList<>();
	private List<PublicInformation> publicInformationsList;

	private StatusEnum gameStatus = StatusEnum.CLEAN;

	Rozgrywka(int numHum, int numBots, int startChips, int wpisowe) {

		numPlayers = numHum + numBots;
		// table = new Table(numHum, numBots, startChips, wpisowe);
		currentMax = wpisowe;

		playerWindowsList = new ArrayList<PlayerWindow>();
		publicInformationsList = new ArrayList<PublicInformation>();
		// Dodawanie humanow
		for (Player p : table.getPlayers()) {
			int id = table.getPlayers().indexOf(p);
			PublicInformation tempPublicInformation = new PublicInformation();
			tempPublicInformation.playerID = Integer.toString(id);

			if (p.isHuman()) {
				PlayerWindow window = new PlayerWindow(numPlayers, table, Integer.toString(id), p);
				playerWindowsList.add(window);

				window.powiadomUzytkownika("<System> Dzięki, że do nas dołączyłeś !\n" + "Podaj nam proszę swój PlayerID\n");

			} else {
				tempPublicInformation.bot = true;
			}

			p.joinGame();
			p.publicInfo = tempPublicInformation;
			tempPublicInformation.allChips = startChips;
			tempPublicInformation.chipsBidded = 0;
			tempPublicInformation.setPlayerStatus("CLEAN");
			publicInformationsList.add(tempPublicInformation);
		}

		ustawWpisowe(wpisowe);
		informAll("Nadałem początkowe wartości.");
		updateAllTableInformation();
		currentMax = wpisowe;

		// playerWindowsList.get(0).powiadomUzytkownika("Napisz mi jakiś tekst\n");
		// String odpowiedz = playerWindowsList.get(0).getOutputMessage();
		//
		// System.out.println(odpowiedz);
		int startingPlayer = table.setStartingPlayer(-1);
		while (true) {
			informAll("Zaczyna player: " + startingPlayer);

			cleanStatus(true);
			licytacja();
			if ((this.gameStatus != StatusEnum.ALL_IN) && (table.getPlayersInGame().size() > 1)) {
				cleanStatus(false);
				wymianaKart();
				informAll("Rozpoczecie drugiej licytacji, rozpoczyna player " + table.getPlayersInGame().get(0).getPlayerID());
				licytacja();
			}

			if (table.getPlayersInGame().size() > 0) {

				List<Integer> winners = Judge.selectWinners(table.getPlayersInGame());
				for (int i = 0; i < winners.size(); ++i) {
					System.out.println("System: Player " + (winners.get(i) + 1) + "<< wygrywa");
					if (table.getWinners().size() == 1) {
						Player oneWinner = table.getPlayersInGame().get(winners.get(i));
						oneWinner.increaseScore();
						System.out.println("Gracz otrzymuje " + table.getPool());
						JOptionPane.showMessageDialog(null, "Wygral gracz " + (oneWinner.getPlayerID()) + " Otrzymuje " + table.getPool());
						table.setCoinsIfOnePlayerWin();
						int zi = 0;
					} else {
						JOptionPane.showMessageDialog(null, "remis");
						System.out.println("Jest remis. Pula gry przechodzi do nastepnej rundy");// TODO
					}
				}
			} else {
				JOptionPane.showMessageDialog(null, "wszyscy spasowali");
			}
			String rank = "";

			// table.getPlayersInGame().clear();
			for (Player p : table.getPlayers()) {
				rank += ("Gracz " + p.getPlayerID() + " posiada " + p.getOwnChips() + " zetonow oraz " + p.getScore() + " punktow \n");

			}
			table.getPlayersInGame().clear();
			for (Player p : table.getPlayers()) {
				if (p.getOwnChips() >= wpisowe)
					p.joinGame();
			}
			JOptionPane.showMessageDialog(null, rank);

			table.sprawdzCzyGraczeMajaWystarczajacaIloscZetonow();
			boolean humansplaying = false;
			for (Player p : table.getPlayersInGame())
				if (p.isHuman())
					humansplaying = true;

			if ((table.getPlayersInGame().size() < 2) || !humansplaying) {
				JOptionPane.showMessageDialog(null, "Koniec gry - mniej niz 2 graczy lub same boty");
				return;
			}

			for (PublicInformation p : this.publicInformationsList) {
				for (Player pl : table.getPlayers()) {
					pl.resetStatus();
					if (pl.getPlayerID() == Integer.parseInt(p.playerID)) {
						p.allChips = pl.getOwnChips();
						p.chipsBidded = 0;

						if (!table.getPlayersInGame().contains(pl))
							p.setPlayerStatus("Nie gra");
						else
							p.setPlayerStatus("CLEAN");
					}

				}
			}
			startingPlayer = table.setStartingPlayer(startingPlayer);
		}

	}

	private void wymianaKart() {
		informAll("Wymien karty");
		for (Player p : table.getPlayersInGame()) {
			if (p.isHuman()) {

				playerWindowsList.get(p.getPlayerID()).changeCards();
			} else {
				((Bot) p).changeCardsUsingStrategy();
			}
		}
	}

	private void licytacja() {
		Boolean czyKoniec = false;
		while (!czyKoniec) {
			ArrayList<Player> inverseList = new ArrayList<Player>(table.getPlayersInGame());
			Collections.copy(inverseList, table.getPlayersInGame());
			Collections.reverse(inverseList);
			for (int i = inverseList.size() - 1; i >= 0; --i) {
				Player p = inverseList.get(i);
				// Pomijamy gracza z allin
				if (p.getPlayerStatus() == StatusEnum.ALL_IN)
					continue;

				int id = p.getPlayerID();
				PublicInformation in = p.publicInfo;
				try {
					if (!in.bot)
						in = makeMoveHuman((Human) p);
					else
						in = askBotWhatMove((Bot) p);

				} catch (ExceptionsInGame ex) {
					System.out.println("fail");
				}
				publicInformationsList.set(id, in);
				// System.out.println(in.playerStatus + "<<<");
				setGameStatus(in);
				informAll("Player" + id + ((in.bot) ? "(BOT)" : "") + " wykonal " + in.playerStatus);
				updateAllTableInformation();
				if (in.getPlayerStatus() != "FOLD")
					table.setStatus(StatusEnum.valueOf(in.getPlayerStatus()));
			}
			czyKoniec = table.isBiddingOver();// czyKoniecLicytacji();
		}

	}

	private PublicInformation askBotWhatMove(Bot bot) throws ExceptionsInGame {
		PublicInformation in = publicInformationsList.get(bot.getPlayerID());
		StatusEnum lastMove = bot.makeMove();
		int bidded = playersMove(lastMove, bot);

		if (lastMove == StatusEnum.BET) {
			in.setPlayerStatus("BET");
			in.chipsBidded = bidded;
			System.out.println("wszlo do bet");
			return in;
		}
		if (lastMove == StatusEnum.RAISE) {
			in.setPlayerStatus("RAISE");
			in.chipsBidded = bidded;
			return in;
		}
		if (lastMove == StatusEnum.CHECK) {
			in.setPlayerStatus("CHECK");
			in.chipsBidded = currentMax;
			return in;
		}
		if (lastMove == StatusEnum.CALL) {
			in.setPlayerStatus("CALL");
			in.chipsBidded = currentMax;
			return in;
		}
		if (lastMove == StatusEnum.FOLD) {
			in.setPlayerStatus("FOLD");
			return in;
		}
		if (lastMove == StatusEnum.ALL_IN) {
			in.setPlayerStatus("ALL_IN");
			in.chipsBidded = in.allChips;
			return in;
		}
		throw new ExceptionsInGame("Zly ruch");
	}

	private int playersMove(StatusEnum move, Player player) throws ExceptionsInGame {

		int howMuch = 0;
		switch (move) {
		case CHECK:
			player.check();
			break;
		case BET:
			if (!player.isHuman()) {
				Bot bot = (Bot) player;
				int chips = bot.chipsToRaise();
				player.bet(chips);
				return (chips);
			} else if (player.isHuman()) {
				System.out.println("System: Okresl wysokosc stawki ");
				// howMuch = Integer.parseInt(getAnswerForMoney(scanIn));
				System.out.println("Player: BET ustawiam na " + howMuch);
				player.bet(howMuch);
				break;
			}

		case RAISE:
			if (!player.isHuman()) {
				Bot bot = (Bot) player;
				int chips = bot.chipsToRaise();
				bot.raise(chips);
				return (chips);

			} else if (player.isHuman()) {
				System.out.println("System: Okresl wysokosc stawki");
				// howMuch = Integer.parseInt(getAnswerForMoney(scanIn));
				System.out.println("Player: RAISE ustawiam na " + howMuch);
				player.raise(howMuch);
				break;
			}
		case ALL_IN:
			player.allin();
			break;
		case CALL:
			player.call();
			break;
		case FOLD:
			player.fold();
			break;
		default:
			break;
		}
		return -1;
	}

	private void cleanStatus(boolean newRound) {
		table.setStatus(StatusEnum.CLEAN);
		for (Player p : table.getPlayers()) {
			if ((newRound) || (p.getPlayerStatus() != StatusEnum.FOLD))
				p.resetStatus();
		}
		gameStatus = StatusEnum.CLEAN;
	}

	private boolean inGame(PublicInformation in) {
		if ((in.playerStatus == "ALL_IN") || (in.playerStatus == "FOLD"))
			return false;

		return true;
	}

	void setGameStatus(PublicInformation in) {
		System.out.println("in.playerstatu" + in.playerStatus);
		if (in.playerStatus.equals("BET")) {
			this.gameStatus = StatusEnum.BET;
			this.currentMax = in.chipsBidded;
		}
		if (in.playerStatus.equals("FOLD")) {
			;
		}
		if (in.playerStatus.equals("ALL_IN")) {
			this.gameStatus = StatusEnum.ALL_IN;
		}
		if (in.playerStatus.equals("CALL")) {
			this.gameStatus = StatusEnum.CALL;
			this.currentMax = in.chipsBidded;
		}
		if (in.playerStatus.equals("RAISE")) {
			this.gameStatus = StatusEnum.RAISE;
			this.currentMax = in.chipsBidded;
		}
		if (in.playerStatus.equals("CHECK")) {
			this.gameStatus = StatusEnum.CHECK;
		}
	}

	List<ActionsEnum> possibleMoves() {
		List<ActionsEnum> tempList = new ArrayList<ActionsEnum>();
		if (gameStatus.equals(StatusEnum.BET)) {
			tempList.add(ActionsEnum.ALL_IN);
			tempList.add(ActionsEnum.CALL);
			tempList.add(ActionsEnum.CHECK);
			tempList.add(ActionsEnum.RAISE);
			tempList.add(ActionsEnum.FOLD);
		}
		if (gameStatus.equals(StatusEnum.ALL_IN)) {
			tempList.add(ActionsEnum.ALL_IN);
			tempList.add(ActionsEnum.FOLD);
		}
		if (gameStatus.equals(StatusEnum.CHECK)) {
			tempList.add(ActionsEnum.ALL_IN);
			tempList.add(ActionsEnum.CALL);
			tempList.add(ActionsEnum.CHECK);
			tempList.add(ActionsEnum.RAISE);
			tempList.add(ActionsEnum.FOLD);
		}
		if (gameStatus.equals(StatusEnum.CALL)) {
			tempList.add(ActionsEnum.ALL_IN);
			tempList.add(ActionsEnum.CALL);
			tempList.add(ActionsEnum.RAISE);
			tempList.add(ActionsEnum.FOLD);
		}
		if (gameStatus.equals(StatusEnum.RAISE)) {
			tempList.add(ActionsEnum.ALL_IN);
			tempList.add(ActionsEnum.CALL);
			tempList.add(ActionsEnum.RAISE);
			tempList.add(ActionsEnum.FOLD);
		}
		if (gameStatus.equals(StatusEnum.CLEAN)) {
			tempList.add(ActionsEnum.BET);
			tempList.add(ActionsEnum.ALL_IN);
			tempList.add(ActionsEnum.CHECK);
			tempList.add(ActionsEnum.FOLD);
		}
		return tempList;
	}

	// updatowanie informacji w table
	void updateAllTableInformation() {
		// for (int i = 0; i < publicInformationsList.size(); ++i) {
		// updateTableInformation(i);
		// }
	}

	Boolean czyKoniecLicytacji() {
		int j = 0;
		for (int i = 0; i < publicInformationsList.size(); ++i) {
			if (publicInformationsList.get(i).playerStatus.equals("CHECK") || publicInformationsList.get(i).playerStatus.equals("FOLD"))
				++j;
		}
		if (j == publicInformationsList.size())
			return true;
		j = 0;
		for (int i = 0; i < publicInformationsList.size(); ++i) {
			if (publicInformationsList.get(i).playerStatus.equals("ALL_IN") || publicInformationsList.get(i).playerStatus.equals("FOLD"))
				++j;
		}
		if (j == publicInformationsList.size())
			return true;
		return false;
	}

	// void updateTableInformation(int playerIndex) {
	// table.updatePlayerInformation(playerIndex,
	// publicInformationsList.get(playerIndex));
	// }
	//
	// PublicInformation getTableInformation(int playerIndex) {
	// return table.getPlayerInformation(playerIndex,
	// publicInformationsList.get(playerIndex));
	// }

	void ustawWpisowe(int wpisowe) {
		for (int i = 0; i < publicInformationsList.size(); ++i) {
			PublicInformation temp = publicInformationsList.get(i);
			temp.chipsBidded = wpisowe;
			publicInformationsList.set(i, temp);
		}
	}

	// void make move
	PublicInformation makeMoveHuman(Human player) throws ExceptionsInGame {
		List<ActionsEnum> possibleActions = null;
		try {
			int index = player.getPlayerID();
			return playerWindowsList.get(index).makeMoveHuman(currentMax, possibleMoves(), publicInformationsList.get(index));
		} catch (ExceptionsInGame e) {
			throw new ExceptionsInGame("Cos poszlo nie tak w makemove");
		}// Jak cos nie poszlo to zwracamy null
	}

	// void make move
	PublicInformation makeMoveBot(int playerIndex) {
		List<ActionsEnum> possibleActions = null;
		// try {
		return playerWindowsList.get(playerIndex).makeMoveBot(currentMax, possibleActions, publicInformationsList.get(playerIndex));
		// } catch (ExceptionsInGame e) {
		// System.out.println(e.getMessage());
		// }// Jak cos nie poszlo to zwracamy null
		// return null;
	}

	void informAll(String message) {
		for (int i = 0; i < numPlayers; ++i) {
			playerWindowsList.get(i).updatePublicInformations(publicInformationsList, message);
		}
	}
}
